﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.DirectoryServices;
    using System.Diagnostics;
    using System.Threading;
    #endregion

    /// <summary>
    /// AD Group Information
    /// </summary>
    [Serializable]
    public class GroupProperties : UserGroup, IExchangeDistributionListAttributes, IGroupProperties {

        #region Members
        #region Fields
        public static readonly new string[] AttributesToGet;

        public static readonly string ObjectCategoryShortName = "group"; 
        #endregion

        public GroupTypeFlags GroupType { get; private set; }

        /// <summary>
        /// The Active Directory principal that manages the group. Checking the box "Manager can update membership list"
        /// adds an ACE to update the member attribute. This attribute is single-valued, but may contain a group.
        /// If Exchange is installed, use the MSExchCoManagedByLink attribute instead of ManagedBy.
        /// </summary>
        public string ManagedBy { get; private set; }

        /// <summary>
        /// Member is not populated by default.  
        /// To populate the collection, use GetGroupMemberDNs().
        /// </summary>
        public IReadOnlyDictionary<string, int> Member { get; private set; }

        #region IExchangeDistributionListAttributes properties
        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/ms978778%28v=exchg.65%29.aspx
        /// The users who are allowed to send mail to this recipient.
        /// </summary>
        public IReadOnlyList<string> AuthOrig { get; private set; }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/ms955165%28v=exchg.65%29.aspx
        /// A distribution list (DL) whose members may not send to this recipient or send messages over this connector. 
        /// </summary>
        public IReadOnlyList<string> DLMemRejectPerms { get; private set; }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/ms955176(v=exchg.65).aspx
        /// A distribution list (DL) whose members may send to this recipient or send messages over this connector. 
        /// </summary>
        public IReadOnlyList<string> DLMemSubmitPerms { get; private set; }

        /// <summary>
        /// A multi-valued attribute to specify the user account principal distinguished names that may manage the group.
        /// In Exchange 2010, distribution groups cannot be managed by groups; only individual users can manage groups. 
        /// This behavior differs from Microsoft Exchange Server 2003, in which you use groups to manage a distribution group.
        /// In Exchange 2003, group ownership is handled at a different level. 
        /// If you move mailboxes from Exchange 2003 to Exchange 2010, members of a group that managed a distribution group 
        /// in Exchange 2003 can no longer modify the group in Exchange 2010.
        /// </summary>
        public IReadOnlyList<string> MSExchCoManagedByLink { get; private set; }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/ms986045%28v=exchg.65%29.aspx
        /// When enabled for a distribution list (DL), unauthenticated users are rejected.
        /// </summary>
        public IReadOnlyList<string> MSExchRequireAuthToSendTo { get; private set; }

        /// <summary>
        /// If set, specifies the legacyExchangeDN of the designated server where the distribution list will be expanded
        /// </summary>
        public string MSExchExpansionServerName { get; private set; }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/ms869090(v=exchg.65).aspx
        /// Contains objects that may not send to this recipient.
        /// </summary>
        public IReadOnlyList<string> UnAuthOrig { get; private set; }
        #endregion

        #endregion

        #region Constructors
        static GroupProperties() {
            var attributesToGet = new List<string>();
            attributesToGet.AddRange(new string[]
            {
                "groupType",
                "managedBy",
                //"member",
                // Exchange attributes
                "authOrig",
                "dlMemRejectPerms",
                "dlMemSubmitPerms",
                "msExchCoManagedByLink",
                "msExchExpansionServerName",
                "msExchRequireAuthToSendTo",
                "unAuthOrig"
            });

            attributesToGet.AddRange(UserGroup.AttributesToGet);

            AttributesToGet = attributesToGet.Distinct().ToArray();
            attributesToGet = null;
        }

        public GroupProperties() {
            this.Initialize();
        }

        public GroupProperties(DirectoryEntry directoryEntry, bool destroyInputObject = true)
            : base(directoryEntry, destroyInputObject) {
            if (this.GetType().Name == typeof(GroupProperties).Name) {
                this.Attributes = null;
            }
        }

        public GroupProperties(SearchResult result, bool destroyInputObject = true)
            : base(result, destroyInputObject) {
            if (this.GetType().Name == typeof(GroupProperties).Name) {
                this.Attributes = null;
            }
        }

        public GroupProperties(List<string> ldifOutput)
            : base(ldifOutput) {
            if (this.GetType().Name == typeof(GroupProperties).Name) {
                this.Attributes = null;
            }
        }
        #endregion

        #region Methods
        public bool GetGroupMemberDNs() {
            bool success = false;
            try {
                this.Member = ADDataAccess.GroupMethods.GetGroupMemberDNs(this.DN);
                success = true;
            }
            catch (Exception e) {
                Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} Error: {3}", 
                    DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId, CustomExtensions.CurrentMethodName(), e.VerboseExceptionString()));
                throw;
            }

            return success;
        }

        protected override void Initialize() {
            base.Initialize();

            this.ManagedBy = string.Empty;
            this.MSExchExpansionServerName = string.Empty;
            this.Member = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            this.AuthOrig = new List<string>();
            this.UnAuthOrig = new List<string>();
            this.DLMemRejectPerms = new List<string>();
            this.DLMemSubmitPerms = new List<string>();
            this.MSExchCoManagedByLink = new List<string>();
            this.MSExchRequireAuthToSendTo = new List<string>();
        }

        protected override void ProcessResults() {

            try {
                base.ProcessResults();

                if (this.Attributes.ContainsKey("groupType")) {
                    this.GroupType = (GroupTypeFlags)Convert.ToInt32(this.Attributes["groupType"][0]);
                }
                if (this.Attributes.ContainsKey("managedBy")) {
                    this.ManagedBy = this.Attributes["managedBy"][0].ToString();
                }
                if (this.Attributes.ContainsKey("msExchExpansionServerName")) {
                    this.MSExchExpansionServerName = this.Attributes["msExchExpansionServerName"][0].ToString();
                }

                this.AuthOrig = ADDataAccess.GetMultiValuedAttribute<string>("authOrig", this.Attributes);
                this.UnAuthOrig = ADDataAccess.GetMultiValuedAttribute<string>("unAuthOrig", this.Attributes);
                this.DLMemRejectPerms = ADDataAccess.GetMultiValuedAttribute<string>("dlMemRejectPerms", this.Attributes);
                this.DLMemSubmitPerms = ADDataAccess.GetMultiValuedAttribute<string>("dlMemSubmitPerms", this.Attributes);
                this.MSExchCoManagedByLink = ADDataAccess.GetMultiValuedAttribute<string>("msExchCoManagedByLink", this.Attributes);
                this.MSExchRequireAuthToSendTo = ADDataAccess.GetMultiValuedAttribute<string>("msExchRequireAuthToSendTo", this.Attributes);

                this.IsValid = !string.IsNullOrWhiteSpace(this.DN)
                    && this.ObjectClass.Any(x => string.Equals(x, ObjectCategoryShortName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception e) {
                Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} Error: {3}", 
                    DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId, CustomExtensions.CurrentMethodName(), e.VerboseExceptionString()));
                throw;
            }
        }
        #endregion
    }
}
